#ifndef _ASYNCLOGGERCTRL_HPP_
#define _ASYNCLOGGERCTRL_HPP_
#include "config.hpp"
#include <mutex>
#include <condition_variable>
#include <thread>
#include <functional>
#include <unistd.h>
#include <vector>
#include <iostream>
#include <cassert>
namespace __mylog
{
    /*异步线程任务池（一段字符缓冲区）*/
    class AsyncLoggerCharBuf
    {
    private:
        size_t _wptr;             // 写的位置
        size_t _rptr;             // 读的位置
        std::vector<char> _space; // 缓冲区空间
    public:
        AsyncLoggerCharBuf()
            : _wptr(0), _rptr(0), _space(DEFAULT_BUFFER_LEN)
        {
        }
        /*向缓冲区里写数据*/
        void push(const char *data, size_t len)
        {
            // len: 写进来的数据的长度
            // writableSize: 还能写的长度
            assert(len <= writableSize());
            std::copy(data, data + len, &_space[_wptr]);
            _wptr += len;
        }
        /*获取读位置空间的地址*/
        const char *readBegin()
        {
            return &_space[_rptr];
        }
        /*当前缓冲区可读的大小*/
        size_t readEnd()
        {
            return _wptr - _rptr;
        }
        /*当前缓冲区可写的大小*/
        size_t writableSize()
        {
            return _space.size() - _wptr;
        }
        /*清空缓冲区*/
        void clear()
        {
            _rptr = 0;
            _wptr = 0;
        }
        /*交换两个缓冲区（this、cbuf）的内容*/
        void swap(AsyncLoggerCharBuf &cbuf)
        {
            this->_space.swap(cbuf._space);
            std::swap(this->_wptr, cbuf._wptr);
            std::swap(this->_rptr, cbuf._rptr);
        }
        /*缓冲区里是否有数据*/
        bool empty()
        {
            return _rptr == _wptr;
        }
    };
}

namespace __mylog
{
    /*异步线程控制器*/
    class AsyncLoggerCtrl
    {
    public:
        using ptr = std::shared_ptr<AsyncLoggerCtrl>;
        using sink_func_t = std::function<void(AsyncLoggerCharBuf &buf)>; // 日志落地回调函数的类型

    private:
        AsyncLoggerCharBuf _consumeBuf;       // 消费者缓冲区
        AsyncLoggerCharBuf _produceBuf;       // 生产者缓冲区
        std::mutex _mtx;                      // 控制生产者与消费者互斥
        std::condition_variable _consumeCond; // 控制生产者与消费者同步
        std::condition_variable _produceCond; // 控制生产者与消费者同步
        bool _stoped;                         // 退出标志，异步日志控制器要退出时设置为true
        sink_func_t _sink;                    // 实际日志落地的函数，由AsyncLogger传入
        std::thread _consumer;                // 消费者线程(消费掉缓冲区的数据)

    public:
        AsyncLoggerCtrl(sink_func_t sink)
            : _stoped(false)
            , _sink(sink)
            , _consumer(std::thread(&AsyncLoggerCtrl::threadRunning, this))
        {}
        ~AsyncLoggerCtrl()
        {
            _stoped = true;
            // 1.退出时，如果消费缓冲区里还有数据，应该提醒消费者继续消费掉
            _consumeCond.notify_all();
            // 2.回收消费者线程
            _consumer.join();
        }
        /*生产者（生成日志的业务线程）生产数据*/
        void push(const std::string &logMsg)
        {
            // 0.如果异步线程控制器停止工作了，不能继续push
            if (_stoped)
                return;
            // 1.生产时，加锁（我在生产时，你不能交换缓冲区, 其他人也不能生产）。生产条件为：生产缓冲区有空间可写
            {
                std::unique_lock<std::mutex> lock(_mtx);
                _produceCond.wait(lock, [&] {
                        return _produceBuf.writableSize() >= logMsg.size(); 
                    });
                _produceBuf.push(logMsg.c_str(), logMsg.size());
            }
            // 2.生产之后，唤醒消费者线程进行消费
            _consumeCond.notify_all();
        }

    private:
        /*消费者线程工作函数*/
        void threadRunning()
        {
            // sleep(1);
            while (true)
            {
                // 0.如果异步线程控制器不再工作，且生产缓冲区为空，则不再消费
                if (_stoped && _produceBuf.empty()) return;

                // 1.消费时，加锁（我在交换缓冲区时，你不能生产）。消费条件为：生产缓冲区有数据
                {
                    std::unique_lock<std::mutex> lock(_mtx);
                    _consumeCond.wait(lock, [&] {
                            return !_produceBuf.empty() || _stoped; 
                         });
                    // 1.1交换两个缓冲区的内容。
                    _produceBuf.swap(_consumeBuf);
                    // 1.2唤醒生产者线程
                    _produceCond.notify_all();
                }
                
                // 3.消费数据
                _sink(_consumeBuf);
                // 4.消费缓冲区清空
                _consumeBuf.clear();
            }
        }
    };
}

#endif